#include "Common.h"
#include "ShaderProgram.h"
#include "Shader.h"
#include "Utils/Logger/Logger.h"
#include "Utils/Util.h"
#include <iostream>
#include <fstream>
#include <iterator>

using namespace RayFrame;
using namespace std;

IMPL_BUCKET_TYPE(RayFrame, ShaderProgramBucket, ShaderProgram);


ShaderProgram::~ShaderProgram()
{
}

void ShaderProgram::Create(Shader *vs, Shader *fs)
{
  assert( vs && fs );

  m_vs = vs;
  m_ps = fs;

  m_vs->m_program = m_ps->m_program = this;
  internalCreate();
}

void ShaderProgram::Create(STRING vsFilename, STRING psFilename)
{
  assert( !vsFilename.empty() || !psFilename.empty() );

  bool isOK = true;
  
  if(!vsFilename.empty())
  {
    m_vs = CreateVertexShader();
    m_vs->Create(vsFilename);
    m_vs->m_program = this;
    isOK = isOK && m_vs->IsOK();
  }
  else
    Logger::InstanceRef().Error("no vertex shader while creating shader program");
  
  if(!psFilename.empty())
  {
    m_ps = CreatePixelShader();
    m_ps->Create(psFilename);
    m_ps->m_program = this;
    isOK = isOK && m_ps->IsOK();
  }
  else
    Logger::InstanceRef().Error("no pixel shader while creating shader program");
  
  if(isOK)
    internalCreate();
  else
    Logger::InstanceRef().Error("Error while creating shader.");
}

void ShaderProgram::Create(STRING programFilename)
{
  assert( !programFilename.empty() );
  m_filename = programFilename;
  if(!SearchResource(m_filename, "Shaders"))
    return;
  std::ifstream file;
  file.open(m_filename.c_str(), std::ios::in);
  if(!file)
    return;

  // read program source
  assert(m_source.empty());
  m_source = "";
  copy(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(),
    back_inserter(m_source));
  file.close();

  // compile shaders
  bool isOK = true;

  m_vs = CreateVertexShader();
  m_vs->m_filename = m_filename;
  m_vs->m_program = this;
  m_vs->m_source = m_source;
  m_vs->internalCreate();
  isOK = isOK && m_vs->IsOK();

  m_ps = CreatePixelShader();
  m_ps->m_filename = m_filename;
  m_ps->m_program = this;
  m_ps->m_source = m_source;
  m_ps->internalCreate();
  isOK = isOK && m_ps->IsOK();

  if(isOK)
    internalCreate();
  else
    ErrorMsg("Error while creating shader.");
}

void ShaderProgram::Destroy()
{
  internalDestroy();

  m_vs->Destroy();
  m_ps->Destroy();
}

ShaderProgram* ShaderProgram::GetDefaultShaderProgram()
{
  ShaderProgram* result;
  result = g_renderer->CreateShaderProgram();
  result->Create("normal.glsl.vs", "normal.glsl.ps");
  return result;
}

ShaderProgram* ShaderProgram::CreateFromFile(STRING vs, STRING ps)
{
  ShaderProgram* program = g_renderer->CreateShaderProgram();
  program->Create(vs, ps);
  return program;
}
